<?php
//php严格模式
declare(strict_types=1);

namespace app\admin\controller;


use app\admin\model\ListsFieldsDisplay;
use app\admin\model\RoleReUser;
use app\BaseController;
use think\App;
use think\facade\Cache;
use think\facade\Db;
use think\facade\Request;
use app\admin\facade\JwtAuth;
use app\admin\service\User as UserS;



class Base extends BaseController
{
    protected $run_module = ""; //当前模块
    protected $run_controller = ""; //当前控制器
    protected $run_action = ""; //当前方法
    protected $user_id = 0; //登录用户id
    protected $is_super_admin = 1; //是否是超级管理员 0:否 1：是
    protected $role_by_user_ids = []; //用户拥有的角色id

    protected $not_need_login_action = []; //无需登录的方法
    public $index_not_display_fields = 'id,update_time';  //默认值  字段不会在后台列表页，字段选择中显示，特别页面需要再各个控制器设置。

    public $additional_fields_list = []; //有些后端列表页显示的字段是数据表中不存在的，将各个控制器的这些字段各自整理保存，过滤后端列表页字段显示使用

    protected function initialize()
    {
        $this->run_module = app('http')->getName();;
        $this->run_controller = $this->request->controller();
        $this->run_action = $this->request->action();

        try{
            //工序控制器中调用了其他控制器，不需要再次验证
//            $this->authUser();   //验证用户权限
            $this->request->user_id = intval($this->user_id);  //将id赋值在request
        }catch (\Exception $e){
            return re_error_json($e->getMessage());
        }
    }



    /**
     * Desc: 验证用户权限
     * User: 苏鹏
     * Date: 2023/9/20
     * Time: 23:16
     * @return void
     */
    private function authUser()
    {

            //访问地址是否需要登录
            $this->not_need_login_action = array_map('strtolower', $this->not_need_login_action);
            if (!in_array($this->run_action, $this->not_need_login_action))
            {
                //需要登录
                $token = $this->request->header('token');

                if (empty($token))
                {
                    return re_error_json(msg_code(16), 16); //token为空，请重新登录
                } else {
                    //验证是否在黑名单中
                    $UserS = new UserS();
                    $token_blacklist = $UserS->tokenInBlacklist('get');
                    if(!empty($token_blacklist)){
                        $token_blacklist = json_decode($token_blacklist, true);
                        $token_blacklist = $token_blacklist ?: [];
                    }else{
                        $token_blacklist = [];
                    }


                    if(in_array($token, $token_blacklist)){
                        return re_error_json(msg_code(18), 18); //token失效,请重新登录
                    }
                    //redis是否有用户信息，如果存在便不再去解析token
                    $token_cache = $UserS->tokenInfoInRedis('get', ['token'=>$token]);
                    //todo 用户信息更新需要删除对应redis中用户token缓存

                    if($token_cache == false)
                    {
                        //解析token
                        $user_info = JwtAuth::checkToken($token);
                        if ($user_info['status'] == false)
                        {
                            return re_error_json($user_info['msg'], $user_info['code']);
                        }
                        $user_id = $user_info['data']['id'];
                    }else{
                        $token_cache = json_decode($token_cache, true);
                        $user_id = $token_cache['id'];
                    }
                    $this->user_id = $user_id;
                    //是否是超级管理员,非管理员验证权限
                    $admin_ids = get_config_cache('super_admin_ids');
                    if(!in_array($this->user_id, $admin_ids))
                    {
                        //用户拥有的角色
                        $role_re_user_ids = (new RoleReUser())->where('user_id', $this->user_id)->column('role_id');
                        if(empty($role_re_user_ids)){
                            return re_error_json(msg_code(19), 19); //未有此权限！
                        }
                        $this->role_by_user_ids = $role_re_user_ids;
                        //角色权限缓存
                        $role_cache = (new \app\admin\model\Role())->getAdminModelCache();


                        //合并角色权限
                        $role_auth = [];
                        foreach($role_re_user_ids as $k=>$v)
                        {
                            if(!empty($role_cache[$v]))
                            {
                                $role_auth = array_merge($role_auth, $role_cache[$v]);
                            }
                        }

                        $route_array = [];
                        foreach($role_cache as $k=>$v){
                            foreach($v as $key=>$val){
                                if(!empty($val['route']) && $val['route'] != '#'){
                                    $auth_route[] = $val;
                                    $route_array[] = $val['route'];
                                }
                            }
                        }

                        //判断后端访问地址是否拥有权限,vue访问地址绑定后端地址，只需要判断后端地址即可。
                        $run_route = strtolower($this->run_module.'/'.$this->run_controller.'/'.$this->run_action);
                        $route_array = array_map('strtolower',$route_array);
                        if(!in_array($run_route, $route_array))
                        {
                            return re_error_json(msg_code(19), 19); //未有此权限！
                        }
                    }else{
                        $this->is_super_admin = 1;
                    }
                }
            }
    }


    /**
     * @param string $view
     * Desc:
     * User: 苏鹏
     * Date: 2023/8/29
     * Time: 9:10
     * @return mixed
     */
    public function index()
    {
        //根据表单生成查询条件，生成查询查询条件
        $map = $this->_search();

        $desc = input('desc', '');
        $desc = empty($desc) ? 'id desc' : $desc;


        $page = input('page', 1, 'intval');
        $page_size = input('pagesize', 50, 'intval');

        $list_field = input('lists_fields', '');
        (new ListsFieldsDisplay)->setListFields($list_field, $this->user_id, $this->run_controller); //处理用户选择各个控制器中显示指定字段

        $field = '*';
        if (method_exists($this, 'filter')) {
            $this->filter($map, $field, $desc);
        }
        $data = $this->_list($this->run_controller, $map, $field, $desc, $page, $page_size);
        return re_success_json($data);
    }

    /**
     * 根据表单字段生成查询条件
     * 进行列表过滤
     */
    /**
     * @param $model_name 当前模型
     * Desc: 根据表单生成查询条件,进行列表过滤
0     * User: 苏鹏
     * Date: 2023/8/29
     * Time: 9:25
     * @return array
     */
    protected function _search(string $model_name = ""): array
    {
        $model_name = $model_name ?: $this->run_controller;
        //处理多级控制器，“.”问题
        if(strpos($model_name, '.') !== false)
        {
            $model_name_array = explode('.', $model_name);
            $model_name = $model_name_array[0];
        }
        $model_name =  '\\app\\admin\\model\\'.$model_name;

        $model = new $model_name();
        $map = [];
        $db_field = $model->getFields();
        if(!empty($db_field)){
            $db_fields_array = array_keys($db_field);
            foreach($db_fields_array as $key=>$val){
                if (isset($_REQUEST[$val]) && $_REQUEST[$val] != '') {
                    $map[$val] = input($val);
                }
            }
        }
        return $map;
    }

    /**
     * @param string $model_name 模型
     * @param $map 条件
     * @param string $order 排序
     * @param array $select_model ['lists'] 可以自定义查询条件，将模型中查询方法传入，优先调用模型查询方法，方法写在当前model
     * @param array $select_model ['count'] 可以自定义查询条件，将模型中查询方法传入，优先调用模型查询方法，方法写在当前model
     * Desc:
     * User: 苏鹏
     * Date: 2023/8/29
     * Time: 10:23
     */
    protected function _list(string $model_name, $map, $field = '*', string $order = 'id desc', int $page = 1, int $page_size = 50, array $select_model = []): array
    {
        $model_name = $model_name ?: $this->run_controller;
        //处理多级控制器，“.”问题
        if(strpos($model_name, '.') !== false)
        {
            $model_name_array = explode('.', $model_name);
            $model_name = $model_name_array[0];
        }
        $model_name =  '\\app\\admin\\model\\'.$model_name;
        $model = new $model_name();

        //查询总记录数量， 优先使用model定义的方法
        if (empty($select_model['count'])) {
            $count = $model->where($map)->count();
        } else {
            if (!method_exists($model, $select_model['count'])) {
                return re_error_json(msg_code(305)); //方法不存在
            }
            try {
                $count = $model->$select_model['count']($map);
            } catch (\Exception $e) {
                return re_error_json($e->getMessage());
            }
        }

        $lists_result = [];
        if ($count > 0) {
            //优先使用model定义的方法
            if (empty($select_model['lists'])) {
                //分页
                $page = $page > 1 ? $page : 1;
                $offset = ($page - 1) * $page_size;
                $sql = $model->where($map)->field($field)->order($order);
                //page=0 查询全部数据
                if($page == 0){
                    $lists_result = $sql->select();
                }else{
                    $lists_result = $sql->limit($offset, $page_size)->select();
                }
                if(!empty($lists_result)){
                    $lists_result = $lists_result->toArray();
                }
            } else {
                if (!method_exists($model, $select_model['lists'])) {
                    return re_error_json(msg_code(305));//方法不存在
                }
                try {
                    $lists_result = $model->$select_model['list']($map, $field, $order, $page, $page_size);
                } catch (\Exception $e) {
                    return re_error_json($e->getMessage());//方法不存在
                }
            }

            //控制器中处理查询后参数
            if (method_exists($this, 'attachedIndex')) {
                $lists_result = $this->attachedIndex($lists_result);
            }
        }
        $data = [
            'list' => empty($lists_result) ? [] :$lists_result,
            'count' => intval($count),
            'page' => $page,
            'page_size' => $page_size
        ];
        return $data;
    }

    /**
     * Desc: 编辑 必须使用模型场景验证，
     *      前置方法 对接受参数加工，
     *      后置方法 处理添加完成后的逻辑，
     *      添加是已开启事务
     * User: 苏鹏
     * Date: 2023/8/29
     * Time: 12:48
     * @return void
     */
    public function insert(string $model_name = '')
    {
        $data = input('post.');

        try {
            //验证数据
            $model_name = $model_name ?: $this->run_controller ;
            $validate = validate($model_name);
            //必须使用场景验证
            if (!$validate->scene('insert')->check($data)) {
                return re_error_json($validate->getError());
            }

            //前置方法，处理接收到的参数
            if (method_exists($this, 'beforeInsert')) {
                //子类需要修改data使用& beforeInsert(&data)
                $this->beforeInsert($data);
            }

            //开启事务，逻辑中可能存在多表查询
            Db::startTrans();
            //写入数据
            $model_name =  '\\app\\admin\\model\\'.$model_name;
            $model = new $model_name();
            $model->save($data);
            $id = $model->id;
            //后置方法，处理添加完成后的逻辑
            if (method_exists($this, 'finishInsert')) {
                //子类需要修改data使用& finishInsert(&$data)
                $this->finishInsert($data, $id);
            }


            Db::commit();
            return re_success_json();
        } catch (\Exception $e) {
            Db::rollback();
            return re_error_json($e->getMessage());
        }
    }

    /**
     * @param string $model_name
     * Desc:  修改 必须使用模型场景验证，
     *      前置方法 对接受参数加工，
     *      后置方法 处理添加完成后的逻辑，
     *      添加是已开启事务
     * User: 苏鹏
     * Date: 2023/8/29
     * Time: 13:46
     * @return array
     * @throws \think\exception\PDOException
     */
    public function update(string $model_name = '')
    {
        $data = input();

        try {
            //验证数据
            $model_name = $model_name ?: $this->run_controller;
            $validate = validate($model_name);
            //必须使用场景验证
            if (!$validate->scene('update')->check($data)) {

                return re_error_json($validate->getError());
            }

            //开启事务，逻辑中可能存在多表查询
            Db::startTrans();
            //前置方法，处理接收到的参数
            if (method_exists($this, 'beforeUpdate')) {
                //子类需要修改data使用& beforeInsert(&data)
                $this->beforeUpdate($data);
            }
            //写入数据
            $model_name =  '\\app\\admin\\model\\'.$model_name;
            $model = new $model_name();

            $model->saveAll([$data]);
            //后置方法，处理添加完成后的逻辑
            if (method_exists($this, 'finishUpdate')) {
                //子类需要修改data使用& finishInsert(&$data)
                $this->finishUpdate($data);
            }
            Db::commit();

            return re_success_json();
        } catch (\Exception $e) {

            Db::rollback();
            return re_error_json($e->getMessage());
        }
    }

    /**
     * Desc: 详情
     * User: 苏鹏
     * Date: 2023/8/29
     * Time: 13:45
     * @return void
     */
    public function detail($model_name = '')
    {
        $id = input('id', 0, 'intval');
        //验证数据
        $model_name = $model_name ?: $this->run_controller;
        $validate = validate($model_name);
        //必须使用场景验证
        if (!$validate->scene('detail')->check(['id' => $id])) {
            return re_error_json($validate->getError());
        }
        try {
            //前置方法，处理接收到的参数
            Db::startTrans();
            //前置方法，处理接收到的参数
            $field = '*';
            if (method_exists($this, 'beforeDetail')) {
                //子类需要修改data使用& beforeInsert(&data)
                $this->beforeDetail($field);
            }

            $model_name =  '\\app\\admin\\model\\'.$model_name;
            $model = new $model_name();
            $result = $model->where('id', $id)->field($field)->find();
            if(!empty($result)){
                $result = $result->toArray();
            }
            $result = empty($result) ? [] : $result;

            if (method_exists($this, 'attachedDetail')) {
                $this->attachedDetail($result);
            }
            Db::commit();
        }catch (\Exception $e){
            Db::rollback();
            return re_error_json($e->getMessage());
        }

        return re_success_json($result);
    }


    /**
     *
     * @param string $modelname
     * Desc: 修改状态 1：正常，2：禁用，3：删除
     * User: 苏鹏
     * Date: 2023/8/29
     * Time: 14:08
     * @return array
     */
    public function status(string $modelname = "")
    {
        $ids = input('ids', '');  //字符串（json 数组）
        $status = input('status', 1, 'intval');

        $model_name = $modelname ?: $this->run_controller;
        try{
            //验证数据
            $data = ['ids' => $ids, 'status' => $status];
            $validate = validate($model_name);
            //必须使用场景验证
            if (!$validate->scene('status')->check($data)) {
                return re_error_json($validate->getError());
            }
            Db::startTrans();
            $ids = json_decode($ids, true);
            //前置方法
            if (method_exists($this, 'beforeStatus')) {
                $this->beforeStatus($ids, $status);
            }
            $model_name =  '\\app\\admin\\model\\'.$model_name;
            $model = new $model_name();
            $model->where('id', 'in', $ids)->update(['status' => $status]);
            //后置方法
            if (method_exists($this, 'finishStatus')) {
                $this->finishStatus($ids, $status);
            }
            Db::commit();

            return re_success_json(['status'=>$status]);
        }catch (\Exception $e){
            Db::rollback();
            return re_error_json($e->getMessage());
        }
    }


    public function del(string $modelname = "")
    {
        $ids = input('ids', '');  //字符串（json 数组）
        $is_del = request()->get('is_del', 0);

        $model_name = $modelname ?: $this->run_controller;
        try{
            //验证数据
            $data = ['ids' => $ids];
            $validate = validate($model_name);
            //必须使用场景验证
            if (!$validate->scene('del')->check($data)) {
                return re_error_json($validate->getError());
            }

            Db::startTrans();
            $ids = json_decode($ids, true);
            //前置方法
            if (method_exists($this, 'beforeDel')) {
                $this->beforeDel($ids, $is_del);
            }
            $model_name =  '\\app\\admin\\model\\'.$model_name;
            $model = new $model_name();
            $re_data = [
                'is_del' => intval($is_del),
            ];
            if($is_del == 0){
                $data = [];
                foreach($ids as $k=>$v){
                    $data[] = ['id'=>$v, 'status' => 3];
                }
                $model->saveAll($data);
            }else{
                $model->destroy($ids);
            }

            //后置方法
            if (method_exists($this, 'finishDel')) {
                $this->finishDel($ids, $is_del);
            }

            Db::commit();
            return re_success_json($re_data);
        }catch (\Exception $e){
            Db::rollback();
            return re_error_json($e->getMessage());
        }
    }

    /**
     * Desc: 回收站
     * User: 苏鹏
     * Date: 2023/10/23
     * Time: 9:24
     * @return void
     */
    public function recycle()
    {
        $_GET['status'] = 3;
        $this->request->withGet($_GET);
        return $this->index();
    }

    /**
     * Desc: 清空回收站
     * User: 苏鹏
     * Date: 2023/10/23
     * Time: 9:29
     * @return void
     */
    public function clean($modelname = '')
    {
        $ids = input('ids', '');  //字符串（json 数组）
        $is_del = request()->get('is_del', 1, 'intval');

        $model_name = $modelname ?: $this->run_controller;
        try{
            //验证数据
            $data = ['ids' => $ids];
            $validate = validate($model_name);
            //必须使用场景验证
            if (!$validate->scene('clean')->check($data)) {
                return re_error_json($validate->getError());
            }

            Db::startTrans();
            $ids = json_decode($ids, true);
            //前置方法
            if (method_exists($this, 'beforeClean')) {
                $this->beforeClean($ids, $is_del);
            }
            $model_name =  '\\app\\admin\\model\\'.$model_name;
            $model = new $model_name();
            $re_data = [
                'is_del' => intval($is_del),
            ];
            if($is_del == 0){
                $data = [];
                foreach($ids as $k=>$v){
                    $data[] = ['id'=>$v, 'status' => 3];
                }
                $model->saveAll($data);
            }else{
                $model->destroy($ids);
            }

            //后置方法
            if (method_exists($this, 'finishClean')) {
                $this->finishClean($ids, $is_del);
            }

            Db::commit();
            return re_success_json($re_data);
        }catch (\Exception $e){
            Db::rollback();
            return re_error_json($e->getMessage());
        }
    }

    /**
     * @param string $modelname
     * Desc: 恢复回收站数据
     * User: 苏鹏
     * Date: 2023/10/25
     * Time: 22:15
     * @return \think\response\Json|null
     */
    public function recover(string $modelname = "")
    {
        try{
            $ids = input('ids', '');  //字符串（json 数组）
            //验证数据
            $data = ['ids' => $ids];
            $model_name = $modelname ?: $this->run_controller;
            $validate = validate($model_name);
            //必须使用场景验证
            if (!$validate->scene('del')->check($data)) {
                return re_error_json($validate->getError());
            }
            Db::startTrans();
            $ids = json_decode($ids, true);
            //前置方法
            if (method_exists($this, 'beforeDel')) {
                $this->beforeRecover($ids);
            }
            $model_name =  '\\app\\admin\\model\\'.$model_name;
            $model = new $model_name();
            $status = 1;
            $model->where('id', 'in', $ids)->update(['status' => $status]);
            //后置方法
            if (method_exists($this, 'finishDel')) {
                $this->finishRecover($ids);
            }
            Db::commit();
            $re_data = [
                'status' => $status
            ];
            return re_success_json($re_data);
        }catch (\Exception $e){
            Db::rollback();
            return re_error_json($e->getMessage());
        }
    }

}